package com.learn.springframework.v3.servlet;

import com.learn.springframework.annotation.Controller;
import com.learn.springframework.annotation.RequestMapping;
import com.learn.springframework.context.ApplicationContext;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
public class DispatcherServlet extends HttpServlet {


    private final String CONTEXT_CONFIG_LOCATION = "contextConfigLocation";
    /**
     * 保存url和method的对应关系
     */
    private List<HandlerMapping> handlerMappings = new ArrayList<HandlerMapping>();

    private Map<HandlerMapping, HandlerAdapter> handlerAdapters = new HashMap<HandlerMapping, HandlerAdapter>();
    /**
     * Ioc容器上下文
     */
    private ApplicationContext applicationContext = null;

    private List<ViewResolver> viewResolvers = new ArrayList<ViewResolver>();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        // 6、根据URL 委派给具体的调用方法
        try {
            doDispatch(req, resp);
        } catch (Exception e) {
            e.printStackTrace();
            /*resp.getWriter().write("500 Exception,Detail: " + Arrays.toString(e.getStackTrace()));*/
            Map<String, Object> model = new HashMap<>();
            model.put("detail", "500 Exception,Detail: ");
            model.put("stackTrace", Arrays.toString(e.getStackTrace()));
            try {
                processDispatchResult(req, resp, new ModelAndView("500", model));
            } catch (Exception exception) {
                exception.printStackTrace();
            }
        }

    }

    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        // 1、通过从request中拿到url，去匹配一个HandlerMapping
        HandlerMapping handler = getHandler(req);
        if (handler == null) {
            processDispatchResult(req, resp, new ModelAndView("404"));
            return;
        }
        // 1、根据URL拿到对应的Handler
        HandlerAdapter handlerAdapter = getHandlerAdapter(handler);

        // 2、根据HandlerMapping拿到HandlerAdapter
        ModelAndView mv = handlerAdapter.handler(req, resp, handler);

        // 3、根据HandlerAdapter拿到对应的ModelAndView

        // 4、根据ViewResolver视图解析器找到对应view对象

        // 通过view对象渲染页面，并返回
        processDispatchResult(req, resp, mv);
    }

    private void processDispatchResult(HttpServletRequest req, HttpServletResponse resp, ModelAndView mv) throws Exception {
        if (null == mv) {
            return;
        }
        if (this.viewResolvers.isEmpty()) {
            return;
        }
        for (ViewResolver viewResolver : this.viewResolvers) {

            View view = viewResolver.resolveViewName(mv.getViewName());
            view.render(mv.getModel(), req, resp);
            return;
        }
    }

    private HandlerAdapter getHandlerAdapter(HandlerMapping handler) {
        if (this.handlerAdapters.isEmpty()) {
            return null;
        }

        HandlerAdapter handlerAdapter = this.handlerAdapters.get(handler);
        if (handlerAdapter.supports(handler)) {
            return handlerAdapter;
        }
        return null;
    }

    private HandlerMapping getHandler(HttpServletRequest req) {
        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        url = url.replaceAll(contextPath, "").replaceAll("/+", "/");

        for (HandlerMapping handlerMapping : this.handlerMappings) {
            Matcher matcher = handlerMapping.getPattern().matcher(url);
            if (!matcher.matches()) {
                continue;
            }
            return handlerMapping;
        }
        return null;
    }


    @Override
    public void init(ServletConfig config) throws ServletException {

        applicationContext = new ApplicationContext(config.getInitParameter(CONTEXT_CONFIG_LOCATION));


        /**===========MVC==========*/
        initStrategies(applicationContext);

        System.out.println("Spring framework is init");

    }

    //初始化策略
    protected void initStrategies(ApplicationContext context) {
        //多文件上传的组件
//        initMultipartResolver(context);
        //初始化本地语言环境
//        initLocaleResolver(context);
        //初始化模板处理器
//        initThemeResolver(context);
        //handlerMappings
        initHandlerMappings(context);
        //初始化参数适配器
        initHandlerAdapters(context);

        //初始化视图转换器
        initViewResolvers(context);

    }

    private void initViewResolvers(ApplicationContext context) {
        // 拿到模板的存放目录
        String templateRoot = applicationContext.getConfig().getProperty("templateRoot");
        String templateRootPath = this.getClass().getClassLoader().getResource(templateRoot).getFile();

        File templateRootDir = new File(templateRootPath);
        String[] templates = templateRootDir.list();
        for (int i = 0; i < templates.length; i++) {
            // 这里主要是为了兼容多模板所有模仿Spring用list保存
            this.viewResolvers.add(new ViewResolver(templateRoot));
        }
    }

    private void initHandlerAdapters(ApplicationContext context) {
        //
        for (HandlerMapping handlerMapping : handlerMappings) {
            this.handlerAdapters.put(handlerMapping, new HandlerAdapter());
        }
    }

    /**
     * 初始化url 和method的一对一对应关系
     */
    private void initHandlerMappings(ApplicationContext context) {

        if (applicationContext.getBeanDefinitionCount() == 0) {
            return;
        }
        for (String beanName : applicationContext.getBeanDefinitionNames()) {
            Object instance = applicationContext.getBean(beanName);
            Class<?> clazz = instance.getClass();
            if (!clazz.isAnnotationPresent(Controller.class)) {
                continue;
            }
            // 保存写在类上面@RequestMapping("/demo")
            String baseUrl = "";
            if (clazz.isAnnotationPresent(RequestMapping.class)) {
                RequestMapping requestMapping = clazz.getAnnotation(RequestMapping.class);
                baseUrl = requestMapping.value();
            }
            // 默认获取所有的public方法
            for (Method method : clazz.getMethods()) {
                if (!method.isAnnotationPresent(RequestMapping.class)) {
                    continue;
                }
                RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
                String regex = ("/" + baseUrl + "/" + requestMapping.value())
                        .replaceAll("\\*", ".*")
                        .replaceAll("/+", "/");
                Pattern pattern = Pattern.compile(regex);
                handlerMappings.add(new HandlerMapping(pattern, instance, method));
                log.info("Mapped " + regex + "," + method);
            }
        }
    }
}
